package cn.zcltd.btg.set.sourcemanager;

import cn.zcltd.btg.set.ParamType;
import cn.zcltd.btg.set.core.Config;
import cn.zcltd.btg.set.core.Module;
import cn.zcltd.btg.set.core.Param;
import cn.zcltd.btg.set.exception.BtgSetRuntimerException;
import cn.zcltd.btg.set.util.StringUtil;
import cn.zcltd.btg.sutil.EmptyUtil;
import cn.zcltd.btg.sutil.FileUtil;
import cn.zcltd.btg.sutil.filescanner.FileScanner;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import java.io.File;
import java.util.*;

/**
 * 配置加载器：从xml文件中获取配置
 */
public class XmlFileSourceManager extends SourceManager {

    private String folderPath; //文件存放路径
    private String fileEncoding; //文件编码

    public XmlFileSourceManager(String name, String folderPath) {
        super(name);
        this.folderPath = folderPath;
    }

    public XmlFileSourceManager(String name, String folderPath, String fileEncoding) {
        super(name);
        this.folderPath = folderPath;
        this.fileEncoding = fileEncoding;
    }

    public String getFolderPath() {
        return folderPath;
    }

    public void setFolderPath(String folderPath) {
        this.folderPath = folderPath;
    }

    public String getFileEncoding() {
        return fileEncoding;
    }

    public void setFileEncoding(String fileEncoding) {
        this.fileEncoding = fileEncoding;
    }

    @Override
    public void init(boolean devModel) {
        setDevMode(devModel);

        File fileFolder = new File(this.folderPath);
        FileUtil.mkDir(fileFolder);
        if (!fileFolder.isDirectory()) {
            throw new RuntimeException(this.folderPath + " not a directory");
        }

        List<Config> configs = loadAll();
        for (Config config : configs) {
            getCache().put(config.getId(), config);
        }

        log.debug("init success");
    }

    @Override
    public List<Config> loadAll() {
        List<Config> configs = new ArrayList<Config>();
        Map<String, Config> configMapLoaded = new HashMap<String, Config>();

        List<File> files = FileScanner.findFiles(this.folderPath, ".*\\.xml");
        for (File file : files) {
            Config config = this.loadFromFile(file);
            if (configMapLoaded.get(config.getId()) != null) {
                throw new RuntimeException(config.getId() + " already exists");
            }
            configs.add(config);
            configMapLoaded.put(config.getId(), config);
        }

        return configs;
    }

    @Override
    public Config load(String configId) {
        String fileName = configId + ".xml";

        List<File> files = FileScanner.findFiles(this.folderPath, ".*\\.xml");
        for (File file : files) {
            if (fileName.equals(file.getName())) {
                return this.loadFromFile(file);
            }
        }

        return null;
    }

    @Override
    public void save(Config config) {
        if (null == config) {
            throw new BtgSetRuntimerException("config must not be null");
        }

        String filePath = this.folderPath + config.getId() + ".xml";
        String configStr = config.toXml();
        FileUtil.write(filePath, configStr, FileUtil.ENCODING_UTF_8);

        getCache().put(config.getId(), config);
    }

    /**
     * 从文件中加载Config
     *
     * @param file 文件
     * @return 配置对象
     */
    private Config loadFromFile(File file) {
        String configStr = FileUtil.readToString(file, this.fileEncoding);
        return parse(configStr);
    }

    /**
     * 将xml格式配置转化为配置对象
     *
     * @param xmlStr xml格式字符串
     * @return 配置对象
     */
    public static Config parse(String xmlStr) {
        Config config;

        try {
            if (EmptyUtil.isEmpty(xmlStr)) {
                throw new BtgSetRuntimerException("content is empty");
            }

            /*
                读取配置转换为dom
             */
            Document document = DocumentHelper.parseText(xmlStr);
            Element rootElement = document.getRootElement();

            /*
                解析config属性
             */
            String id = rootElement.attributeValue("id");
            if (EmptyUtil.isEmpty(id)) {
                throw new BtgSetRuntimerException("id must not be empty ");
            }
            String name = rootElement.attributeValue("name");
            config = Config.createConfig(id, name);

            String desc = rootElement.attributeValue("desc");
            config.setDesc(desc);

            /*
                解析config>param
             */
            int counterParam = 0;
            for (Iterator i = rootElement.elementIterator("param"); i.hasNext(); ) {
                Element paramElement = (Element) i.next();

                String paramId = paramElement.attributeValue("id");
                if (EmptyUtil.isEmpty(paramId)) {
                    throw new BtgSetRuntimerException("id must not be empty:params[" + counterParam + "]");
                }
                String paramName = paramElement.attributeValue("name");
                String paramDataType = paramElement.attributeValue("type");
                if (EmptyUtil.isEmpty(paramDataType)) {
                    throw new BtgSetRuntimerException("type must not be empty:params[" + counterParam + "]");
                }
                String paramValue = paramElement.getText();

                Param param = config.createParam(paramId, paramName, ParamType.valueOf(paramDataType), paramValue);

                String paramDesc = paramElement.attributeValue("desc");
                param.setDesc(paramDesc);

                String paramDatePattern = paramElement.attributeValue("datePattern");
                param.setDatePattern(paramDatePattern);

                String paramRemark = paramElement.attributeValue("remark");
                param.setRemark(paramRemark);

                counterParam++;
            }

            /*
                解析config>module
            */
            int counterModule = 0;
            for (Iterator i = rootElement.elementIterator("module"); i.hasNext(); ) {
                Element moduleElement = (Element) i.next();

                String moduleId = moduleElement.attributeValue("id");
                if (EmptyUtil.isEmpty(moduleId)) {
                    throw new BtgSetRuntimerException("id must not be empty:modules[" + counterModule + "]");
                }
                String moduleName = moduleElement.attributeValue("name");

                Module module = config.createModule(moduleId, moduleName);

                String moduleDesc = moduleElement.attributeValue("desc");
                module.setDesc(moduleDesc);

                /*
                    解析config>mofule>param
                 */
                int counterModuleParam = 0;
                for (Iterator j = moduleElement.elementIterator("param"); j.hasNext(); ) {
                    Element moduleParamElement = (Element) j.next();

                    String paramId = moduleParamElement.attributeValue("id");
                    if (EmptyUtil.isEmpty(paramId)) {
                        throw new BtgSetRuntimerException("id must not be empty:modules[" + i + "]>params[" + counterModuleParam + "]");
                    }
                    String paramName = moduleParamElement.attributeValue("name");
                    String paramDataType = moduleParamElement.attributeValue("type");
                    if (EmptyUtil.isEmpty(paramDataType)) {
                        throw new BtgSetRuntimerException("type must not be empty:modules[" + i + "]>params[" + counterModuleParam + "]");
                    }
                    String paramValue = moduleParamElement.getText();

                    Param moduleParam = module.createParam(paramId, paramName, ParamType.valueOf(paramDataType), paramValue);

                    String paramDesc = moduleParamElement.attributeValue("desc");
                    moduleParam.setDesc(paramDesc);

                    String paramDatePattern = moduleParamElement.attributeValue("datePattern");
                    moduleParam.setDatePattern(paramDatePattern);

                    String paramRemark = moduleParamElement.attributeValue("remark");
                    moduleParam.setRemark(paramRemark);

                    counterModuleParam++;
                }

                counterModule++;
            }
        } catch (Exception e) {
            throw new BtgSetRuntimerException("load error:" + e.getMessage(), e);
        }

        return config;
    }
}